#include "SDLEngine.h"
#include <iostream>
#include "Timer.h"
#include "../gui/ActiveWindowsFactory.h"
#include "../gui/WindowHelper.h"
#include "widgets/SurfaceFactory.h"

SDLEngine::SDLEngine(std::string screenTitle, int widthResolution,
		int heightResolution, int bitsPerPixel) {
	this->screenTitle = screenTitle;
	this->widthResolution = widthResolution;
	this->heightResolution = heightResolution;
	this->bitsPerPixel = bitsPerPixel;
	this->externalNotify = NULL;

	this->salida = new ColaMensajesSDL();
	this->entrada = new ColaMensajesSDL();

	this->notifier = new Notifier(this,entrada);

	this->windowFactory = new WindowHelper();

	//Seteo el estado
	mutex.unlock();
	running = true;
}

SDLEngine::~SDLEngine() {
	mutex.lock();
	delete (this->screen);
	if (activeWindow != NULL) {
		delete activeWindow;
	}

	delete windowFactory;

	SDL_Quit();
	mutex.unlock();
}

Event* SDLEngine::pollEvent() {
	SDL_Event* sdlEvent = new SDL_Event();


	int ret = 0;

	if(running){
		mutex.lock();
		ret = SDL_PollEvent(sdlEvent);
		mutex.unlock();
	}
	if (ret) {
		return new Event(sdlEvent);
	}

	return NULL;
}

void SDLEngine::filterUselessEvents(){
	//Seteo el filtro de eventos que no me interezan
	SDL_EventState(SDL_ACTIVEEVENT, SDL_IGNORE);			/**< Application loses/gains visibility */
	SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);			/**< Mouse moved */
	SDL_EventState(SDL_JOYAXISMOTION, SDL_IGNORE);		/**< Joystick axis motion */
	SDL_EventState(SDL_JOYBALLMOTION, SDL_IGNORE);		/**< Joystick trackball motion */
	SDL_EventState(SDL_JOYHATMOTION, SDL_IGNORE);		/**< Joystick hat position change */
	SDL_EventState(SDL_JOYBUTTONDOWN, SDL_IGNORE);		/**< Joystick button pressed */
	SDL_EventState(SDL_JOYBUTTONUP, SDL_IGNORE);			/**< Joystick button released */
	SDL_EventState(SDL_SYSWMEVENT, SDL_IGNORE);			/**< System specific event */
}

bool SDLEngine::startEngine() {
	mutex.lock();
	if (SDL_Init(SDL_INIT_EVERYTHING) == -1) {
		return false;
	}
	filterUselessEvents();

	SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);

	//TODO remover este constructor mal implementadoo
	screen = new Surface();
	setVideoMode(this->widthResolution, this->heightResolution,this->bitsPerPixel, SDL_SWSURFACE | SDL_RESIZABLE);

	if (screen == NULL) {
		mutex.unlock();
		return false;
	}
	if (TTF_Init() == -1) {
		mutex.unlock();
		return false;
	}
	char* c_screenTitle = new char[this->screenTitle.size() + 1];
	strcpy(c_screenTitle, this->screenTitle.c_str());
	SDL_WM_SetCaption(c_screenTitle, NULL);
	delete[] c_screenTitle;

	SDL_FillRect(screen->surface, &screen->surface->clip_rect,
			SDL_MapRGB(screen->surface->format, 233, 233, 233));


	//Agrego la ventana de Start Game
	this->activeWindow = windowFactory->startGameWindow();
	mutex.unlock();

	//Inicio el notifier
	this->notifier->start();


	return true;
}

void SDLEngine::stop() {
	mutex.lock();
	running = false;

	notifier->terminar();

	entrada->terminar();
	salida->terminar();

	entrada->signal();
	salida->signal();

	TTF_Quit();
	mutex.unlock();
}


void SDLEngine::setActiveWindow(Window* activeWindow) {
	if (this->activeWindow != NULL) {
		delete this->activeWindow;
	}
	this->activeWindow = activeWindow;
}

//No hace falta lockearlo porque ya viene de metodos con lock
void SDLEngine::refreshScreen() {
	if (SDL_Flip(this->screen->surface) != 0) {
		Logger::log("SDLEngine", "refreshScreen",
				"no se pudo refrescar la pantalla");
	}
}

void SDLEngine::updateVideoMode() {
	mutex.lock();
	setVideoMode(widthResolution, heightResolution, BITS_PER_PIXEL,SDL_SWSURFACE | SDL_RESIZABLE);
	char* c_screenTitle = new char[this->screenTitle.size() + 1];strcpy
	(c_screenTitle, this->screenTitle.c_str());
	SDL_WM_SetCaption(c_screenTitle, NULL);
	delete[] c_screenTitle;
	SDL_FillRect(screen->surface, &screen->surface->clip_rect,SDL_MapRGB(screen->surface->format, 233, 233, 233));

	mutex.unlock();
}

//No hace falta lockearlo porque ya viene de metodos con lock
void SDLEngine::setVideoMode(int width, int height, int bpp, Uint32 flags) {
	screen->rectangle.w = width;
	screen->rectangle.h = height;
	this->screen->surface = SDL_SetVideoMode(width, height, bpp, flags);
}

void SDLEngine::changeScreenTitle(std::string screenTitle) {
	this->screenTitle = screenTitle;
}


void* SDLEngine::run() {
	//Inicio la engine con la ventana inicial
	startEngine();


	//Lanzo el thread de dibujo
	thread = SDL_CreateThread( drawThread, this);


	Event* event;

	while (running) {

		event = notifier->pullEvent();
		if(!event)
			continue;

		//Si hay externalNotify lo agrego
		if(this->externalNotify !=NULL)
			this->externalNotify->setEvent(event);


		switch (event->getType()) {
		case UserEvent:{

			mutex.lock();
			Window* newActiveWindow = ActiveWindowsFactory::switchActiveWindow(*event, this);
			if(newActiveWindow != NULL){
				delete activeWindow;
				this->activeWindow = newActiveWindow;
				mutex.unlock();
				updateVideoMode();
			}
			mutex.unlock();

			//Lo agrego a la cola de salida de mensajes
			//Si tengo external notify, dejo de notificar a la lista normal
			//Si es un exit tengo que informalo para que salga
			if(externalNotify == NULL || event->getCode() == EXIT){
				salida->setEvent(event);

				if(event->getCode() == EXIT)
					this->running = false;
			}

			}break;
		case Resize:
			setNewVideoMode(event->getW(), event->getH());
			updateVideoMode();
			break;
		default:
			activeWindow->handleEvent(*event);
			break;
		}
	}

	return NULL;

}


void SDLEngine::updateActiveWindow() {
	mutex.lock();
	activeWindow->resolve();
	activeWindow->showOn(this->screen->surface);
	refreshScreen();
	mutex.unlock();
}

void SDLEngine::setNewEvent(Event* event){
	this->entrada->setEvent(event);
}

Event *SDLEngine::getLasEvent(){
	return this->salida->getEvent();
}

void SDLEngine::initPlayPanel(BattleMasterPlayerGame* game){
	mutex.lock();
	((PlayScenarioPanel*)activeWindow->getScenarioContainer())->init(game);
	mutex.unlock();
}

void SDLEngine::setPlayPanelNotification(std::string notification){
	((PlayScenarioPanel*)activeWindow->getScenarioContainer())->setNotification(notification);
}

void SDLEngine::removeTank(Tank* tank){
	mutex.lock();
	setPlayPanelNotification(tank->getOwner() + " se ha desconectado a la partida.");

	this->activeWindow->remove(tank);
	mutex.unlock();
}

Tank* SDLEngine::addTank(int posX, int posY, std::string owner,int angle,bool flaged){
	mutex.lock();
	Tank* tank = new Tank(SurfaceFactory::getSurface(SURFACETANK),posX,posY,activeWindow->getScenarioContainer(),owner,angle,flaged);

	this->activeWindow->add(tank);

	setPlayPanelNotification(owner + " se ha incorporado a la partida.");
	mutex.unlock();

	return tank;
}

bool SDLEngine::isRunning(){
	return running;
}

void SDLEngine::addExternalNotify(ColaMensajesSDL* cola){
	this->externalNotify = cola;
}


SDL_Surface* SDLEngine::loadSurface(std::string path){
	SDL_Surface* aux = NULL;
	SDL_Surface* surface = NULL;

	char* c_path = new char[path.size() + 1];
	strcpy	(c_path, path.c_str());
	aux = IMG_Load(c_path);
	delete[] c_path;

	if (aux != NULL) {
		surface = SDL_DisplayFormatAlpha(aux);
		SDL_FreeSurface(aux);
	}

	return surface;
}

TTF_Font* SDLEngine::openFont(std::string path, int size){
	TTF_Font* retorno = TTF_OpenFont(path.c_str(), size);

	return retorno;
}


void SDLEngine::setNewVideoMode(int w, int h){
	this->widthResolution = w;
	this->heightResolution = h;
}



int drawThread( void *ptr ){

	SDLEngine* engine = (SDLEngine*)ptr;

	while(engine->isRunning()){
		engine->updateActiveWindow();
		SDL_Delay(50);

	}

	return 0;
}



